/*
 * @Author: CGT (caogtaa@gmail.com) 
 * @Date: 2020-01-16 22:09:08 
 * @Last Modified by: CGT (caogtaa@gmail.com)
 * @Last Modified time: 2020-01-16 23:46:33
 */

'use strict';

let _menuTemplateCache = null;
let _paramCache = {};

function createNode(uuid) {
	let param = { uuid: uuid };
	Object.assign(param, _paramCache);
	Editor.Scene.callSceneScript('cc-ext-scene-menu', 'create-node', param, (err) => {
		if (err)
			Editor.log(err);
	});
}

function sendCommand(command, param) {
	let p = {};
	Object.assign(p, _paramCache);
	p.customParam = param;
	Editor.Ipc.sendToMain(command, p, null);
}

function generateMenuTemplate(conf) {
	let result = [];
	for (let c of conf) {
		// https://electronjs.org/docs/api/menu
		let item = {};
		item.label = c.name;

		// item.click = createNode.bind(c.uuid);
		// the menu item auto unbound my function, why?
		// so I put uuid in closure
		if (c.type == 0) {
			// prefab
			let uuid = c.uuid;
			item.click = () => {
				createNode(uuid);
			};
		} else if (c.type == 1) {
			// command
			item.click = () => {
				sendCommand(c.command, c.param);
			};
		} else if (c.submenu) {
			item.submenu = generateMenuTemplate(c.submenu);
		} else {
			// unexpected
		}

		result.push(item);
	}

	return result;
}

function loadMenu() {
	const fs = require('fs');
	let configPath = Editor.Project.path + '/scene-menu-config.json';
	if (!fs.existsSync(configPath)) {
		// read default config
		configPath = Editor.url('packages://cc-ext-scene-menu/default-config.json');
	}

	fs.readFile(configPath, function (err, data) {
		if (err) {
			// file not exists
			return;
		}

		try {
			// Editor.log(`main.js read data: ${data}`);
			let config = JSON.parse(data);
			_menuTemplateCache = generateMenuTemplate(config);
		} catch (err) {
			// if any error occur, old template cache is not replaced
		} finally {

		}
	});
}

function injectContextMenu(webContents) {
	if (webContents.__gt_injected) {
		// already injected
		return;
	}

	if (webContents != Editor.Window.main.nativeWin.webContents) {
		// not cc main app window
		return;
	}
	webContents.__gt_injected = true;

	let hackCode = `
    (() => {
      function appendListener(node, eventType, fn = null) {
        node.addEventListener(eventType, (e) => {
          if (fn)	fn(e);
        }, true);		
      }
    
      let getLabelRoot = (gridRoot, className) => {
        for (let c of gridRoot.children) {
          if (c.className === className)
            return c;
        }
    
        return null;
      };
    
      let getPixel = (elem) => {
        return parseFloat(elem.style.transform.match(/(-?[0-9\.]+)/g)[0]);
      };
    
      let getWorldPos = (elem) => {
        return parseFloat(elem.innerText.replace(/,/g, ''));
      };
    
      let pixelToWorld = (labelRoot, pixel) => {
        let pmin = getPixel(labelRoot.firstChild);
        let pmax = getPixel(labelRoot.lastChild);
        let wmin = getWorldPos(labelRoot.firstChild);
        let wmax = getWorldPos(labelRoot.lastChild);
        return (pixel - pmin) * (wmax - wmin) / (pmax - pmin) + wmin;
      };
    
      let svgPosToWorld = (x, y) => {
        let gridRoot = document.getElementById('scene').shadowRoot.getElementById('sceneView').shadowRoot.getElementById('grid').shadowRoot;
        let vLabelRoot = getLabelRoot(gridRoot, 'vLabels');
        let hLabelRoot = getLabelRoot(gridRoot, 'hLabels');
        let worldX = pixelToWorld(hLabelRoot, x+4); // horizontal label offset = 4 (move rightward in svg)
        let worldY = pixelToWorld(vLabelRoot, y-15);  // vertical label offset = -15 (move upward in svg)
        return [worldX, worldY];
      };

      let svgNode = null;
      let downX = 0;
      let downY = 0;
      let isDown = false;
      let postContextMenuMsg = () => {
        let rect = svgNode.getBoundingClientRect();
        downX -= rect.left;
        downY -= rect.top;
        let worldX = 0;
        let worldY = 0;
        try {
          let arr = svgPosToWorld(downX, downY);
          worldX = arr[0];
          worldY = arr[1];
        } catch(error) {}

        Editor.Ipc.sendToMain('cc-ext-scene-menu:on-context-menu', 
          {x: downX, y: downY, worldX: worldX, worldY: worldY}, null);
      };
    
      appendListener(document, 'mousedown', (e) => {
        if (e.button != 2)
          return;

        // check if inside svg view
        if (!svgNode)
          svgNode = document.getElementById('scene').shadowRoot.getElementById('sceneView').shadowRoot.getElementById('gizmosView').shadowRoot.getElementById('SvgjsSvg1000');
        
        if (!svgNode) {
          Editor.log('svg view not ready');
          return;
        }
    
        let rect = svgNode.getBoundingClientRect();
        if (e.pageX >= rect.left && e.pageX < rect.right && e.pageY >= rect.top && e.pageY < rect.bottom) {
          downX = e.pageX;
          downY = e.pageY;
          isDown = true;
        }
      });
    
      appendListener(document, 'mouseup', (e) => {
        if (e.button == 2 && isDown && e.pageX == downX && e.pageY == downY) {
          isDown = false;
          postContextMenuMsg();
        }
      });
    })();

    1+2+3
  `;

	webContents.executeJavaScript(hackCode, function (result) {
		// result = 6
	});
}

if (!Editor.__Menu__) {
	Editor.__Menu__ = Editor.Menu;
};

class CustomMenu extends Editor.__Menu__ {
	constructor(template, webContent) {
		//打印编辑器默认菜单数据
		// Editor.log(template);

		let menuLocation;//菜单所在区域

		//判断是哪种菜单，暂时没有找到很优雅的办法
		//构造函数的template是编辑器自带的菜单配置数组，
		//可以添加/删除/重写template的元素实现自定义菜单功能
		if (template.length > 0) {
			let first = template[0];
			if (first.label == "创建节点")//场景节点右键菜单
				menuLocation = "node";
			else if (first.label == "新建")//asset右键菜单
				menuLocation = "asset";
			else if (first.label == "Remove")//脚本组件菜单
				menuLocation = "component";
			else if (first.path && first.path.startsWith("渲染组件"))//添加组件菜单
				menuLocation = "addcomponent";
			//还有其他区域的菜单比如控制台面板菜单，就不再列举了
		}

		if (menuLocation == "asset") {
			//TODO 在这里插入asset右键菜单
			let assetInfo = getSelectedFirstAssetInfo();

			//Editor.log(assetInfo);

			template.push({ type: "separator" });
			template.push({
				label: '复制资源 UUID', click: () => {
					const clipboard = Editor.require('electron').clipboard;
					clipboard.writeText(assetInfo.uuid);
					Editor.log(assetInfo.uuid);
				}
			});
			template.push({
				label: '复制资源路径', click: () => {
					const clipboard = Editor.require('electron').clipboard;
					clipboard.writeText(assetInfo.url);
					Editor.log(assetInfo.url);
				}
			});

		}
		else if (menuLocation == "node") {
			//在这里插入场景节点右键菜单
			let node_selection = Editor.Selection.curSelection("node");
			let insertIndex = 1;

			template.splice(insertIndex++, 0, { type: "separator" });
			template.splice(insertIndex++, 0, { label: "创建Sprite（精灵）", click: template[0].submenu[1].submenu[0].click });
			template.splice(insertIndex++, 0, { type: "separator" });

			let groupMenuEnable = true;
			let groupMenu = { label: "测试二级菜单", enabled: true, submenu: [] };

			template.splice(insertIndex++, 0, groupMenu);

			groupMenu.submenu.push({
				label: "测试二级菜单1", enabled: groupMenuEnable, click: () => {
					Editor.log("测试二级菜单1");
				}
			});
			groupMenu.submenu.push({
				label: "测试二级菜单2", enabled: groupMenuEnable, click: () => {
					Editor.log("测试二级菜单2");
				}
			});

		}
		else if (menuLocation == "component") {
			//在这里插入组件菜单，可传递节点uuid，
			let params = template[0].params;
			//Editor.log(params);
			template.push({ type: "separator" });
			template.push({
				label: '测试组件脚本菜单', enabled: true, click: () => {
					Editor.log("测试组件脚本菜单");
				}
			});
		}
		else if (menuLocation == "addcomponent") {
			//在这里插入添加组件菜单，可传递节点uuid
			let params = template[0].params;
			let nodeUuid = params[0];

			//添加选中节点的同名脚本
			template.unshift({ type: "separator" });
			template.unshift({
				label: '测试添加脚本菜单', enabled: true, click: () => {
					Editor.log("测试添加脚本菜单");
				}
			});
		}

		super(template, webContent);
	}

}

/**
* 获取资源管理器中选中的第一个资源
* @returns 
*/
function getSelectedFirstAssetInfo() {
	let asset_selection = Editor.Selection.curSelection("asset");
	if (asset_selection == null || asset_selection.length == 0) {
		return null;
	}

	let info = Editor.assetdb.assetInfoByUuid(asset_selection[0]);

	return info;
}

module.exports = {
	load() {
		loadMenu();
		try {
			if (Editor.Window.main.nativeWin.webContents.__gt_injected) {
				// in case plugin if reloaded
				return;
			}
		} catch (error) {
			// usually happen when creator is just started and main window is not created
			Editor.log(error);
		}

		// todo: 如果插件是中途加载的，判断webContents如果就绪了就注入
		const electron = require('electron');
		let injectFn = injectContextMenu;
		electron.app.on('web-contents-created', (sender, webContents) => {
			webContents.on('dom-ready', (e) => {
				injectFn(e.sender);
			});
		});

		Editor.Menu = CustomMenu;//应用自定义菜单
	},

	unload() {
		// let webContenst = Editor.Window.main.nativeWin.webContents;
		// if (webContenst.__gt_injected) {
		//     // todo: removeEventListeners
		//     webContenst.__gt_injected = false;
		// }
		// execute when package unloaded
		Editor.Menu = Editor.__Menu__;//还原菜单
	},

	// register your ipc messages here
	messages: {
		'active'() {
			Editor.Menu = CustomMenu;
			Editor.log("已启用自定义上下文菜单");

		},
		'disactive'() {
			Editor.Menu = Editor.__Menu__;
			Editor.log("已停用自定义上下文菜单");
		},

		'create-node'() {
			Editor.Scene.callSceneScript('cc-ext-scene-menu', 'create-node', null, function (err) {
				// Editor.log('create-node finish');
			});
		},
		'on-context-menu'(event, param) {
			param = param || { x: 0, y: 0, worldX: 0, worldY: 0 };
			_paramCache = param;
			if (_menuTemplateCache) {
				Editor.Window.main.popupMenu(_menuTemplateCache, param.x, param.y);
			}
		},
		'custom-context-menu'() {
			Editor.Panel.open('cc-ext-scene-menu');
		},
		'update-context-menu'() {
			loadMenu();
		},
		'say-hello'(event, param) {
			Editor.log(`Hello! param: {worldX = ${param.worldX}, worldY = ${param.worldY}}, customParam = ${param.customParam}`);
		}
	},
};